/*
 * anima/x86_64/mm.hh - x86-64-specific memory management interface.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#ifndef HEADER_ANIMA_X86_64_MM_HH
#define HEADER_ANIMA_X86_64_MM_HH

#include HEADER (x86_64,mmu.hh)
#include HEADER (anima,fwd_mm.hh)
#include HEADER (anima,locks.hh)

START_NAMESPACE2 (anima,x86_64)

class mm : public ::anima::fwd_mm
{
private:
  typedef rw_lock_t<u64_t, 52, 61, 62> pe_rw_lock_t;

public:
  INLINE
  static ::x86_64::pe_t &
  clear (::x86_64::pe_t &pe)
  {
    pe_rw_lock_t::clear (pe.raw);
    return pe;
  }

  INLINE
  static void
  rlock (::x86_64::pe_t &pe)
  {
    ((pe_rw_lock_t *) (void *) &pe)->rlock ();
  }

  INLINE
  static void
  unrlock (::x86_64::pe_t &pe)
  {
    ((pe_rw_lock_t *) (void *) &pe)->unrlock ();
  }

  INLINE
  static void
  wlock (::x86_64::pe_t &pe)
  {
    ((pe_rw_lock_t *) (void *) &pe)->wlock ();
  }

  INLINE
  static void
  unwlock (::x86_64::pe_t &pe)
  {
    ((pe_rw_lock_t *) (void *) &pe)->unwlock ();
  }

  INLINE
  static bool 
  wlock_if_np (::x86_64::pe_t &pe)
  {
    unrlock (pe);
    wlock (pe);

    if (pe.P)
      {
        unwlock (pe);
        rlock (pe);
        return false;
      }

    return true;
  }

public:
  struct tte_t : ::x86_64::pe_t
  {
  public:
    enum flag_t
    {
      ANIMA_MM_TTE_COMMON_FLAGS,
      FLAG_CACHE = ANIMA_MM_TTE_MAX_FLAG << 1,
      FLAG_WT = ANIMA_MM_TTE_MAX_FLAG << 2,
      FLAG_GLOBAL = ANIMA_MM_TTE_MAX_FLAG << 3,
      FLAG_DEFAULTS = FLAG_WRITE | FLAG_CACHE,
      FLAG_IO = 0
    };

  public:
    tte_t (u64_t raw = 0) : ::x86_64::pe_t (raw)
    {
    }

  public:
    INLINE
    flag_t
    get_flags ()
    {
      return (flag_t) ((RW ? FLAG_WRITE : 0) | (NX ? 0 : FLAG_EXEC)
                       | (US ? FLAG_USER : 0) | (PCD ? 0 : FLAG_CACHE)
                       | (PWT ? FLAG_WT : 0));
    }

    INLINE
    tte_t &
    set_flags (flag_t flags)
    {
      set_RW (flags & FLAG_WRITE).set_NX (!(flags & FLAG_EXEC))
        .set_US (flags & FLAG_USER).set_PCD (!(flags & FLAG_CACHE))
        .set_PWT (flags & FLAG_WT);
      return *this;
    }

    INLINE
    ct_t
    get_ct ()
    {
      return (ct_t) (AVL & 3);
    }

    INLINE
    tte_t &
    set_ct (ct_t ct)
    {
      set_AVL ((AVL & 4) | ct);
      return *this;
    }

    INLINE
    bool
    get_I ()
    {
      return (AVL & 4) ? true : false;
    }

    INLINE
    tte_t &
    set_I (bool I = true)
    {
      set_AVL ((AVL & 3) | (I ? 4 : 0));
      return *this;
    }

    INLINE
    tte_t &
    clear ()
    {
      mm::clear (*this);
      return *this;
    }

    INLINE
    void
    rlock ()
    {
      mm::rlock (*this);
    }

    INLINE
    void
    unrlock ()
    {
      mm::unrlock (*this);
    }

    INLINE
    void
    wlock ()
    {
      mm::wlock (*this);
    }

    INLINE
    void
    unwlock ()
    {
      mm::unwlock (*this);
    }
  };

public:
  static const unsigned int PAGE_BITS = 12;
  static const u64_t PAGE_SIZE = U64 (1) << PAGE_BITS;

protected:
  static bool init ();

public:
  static void map_null (u64_t paddr);
  static void unmap_null ();
};

END_NAMESPACE2

#endif /* HEADER_ANIMA_X86_64_MM_HH */

